#include <csp_protocol_msg.h>
#include <csp_shared_types.h>
#include <csp_macros.h>
#include <csp_debug.h>
#include <string.h>
#include <stdint.h>
#include <malloc_align.h>
#include <csp_spu_lib.h>
#include <spu_mfcio.h>
#include <kernel.h>
#include <syscall.h>

#include <stdarg.h>


syscall_pointers_t *kernel_syscalls;

#ifdef __cplusplus
typedef int (*Funcptr) (int, int);
int __csp__syscall(int x, int y){
	Funcptr syscall = (Funcptr) 0x80;
	return syscall(x, y);
}
#else
int __csp__syscall(int x, int y){
	int (*syscall)(int, int) = (void *) 0x80;
	return (*syscall)(x, y);
}
#endif

/* same djb2-hash as in CSPConfig.cpp */
uint64_t __djb2_hash(const char *str) {
	uint64_t hash = 5381;
	int c;
	while ( (c = *str++) )
		hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
	return hash;
}


void csp_timing_start(){
	spu_write_out_intr_mbox(COMM_SW_START);
}

void csp_timing_stop(){
	spu_write_out_intr_mbox(COMM_SW_STOP);
}

int __csp_parallel(int size, char *mnemonics[]){
	int i;
	uint64_t *payload;
	ls_addr_t package[4];


	payload = (uint64_t *) _malloc_align((1+size)*sizeof(uint64_t), 7);
	payload[0] = size;

	package[0] = size;
	package[1] = (ls_addr_t) mnemonics;
	package[2] = (ls_addr_t) payload;

	for(i=0; i < size; i++){
		payload[i+1] = __djb2_hash(mnemonics[i]);
	}

	return __csp__syscall( COMM_INIT_PROCS, (int) package);
}


csp_process csp_create_process(char *mnemonic){
	return mnemonic;
}

int csp_run_process(csp_process process){
	char *p[] = {process};
	return __csp_parallel(1, p);
}

int csp_run_parallel(csp_process procs[], size_t size){
	return __csp_parallel(size, procs);
}


int __csp_chan_comm(int op, uint32_t chan_type, uint64_t chan_id, 
		void *addr, size_t nbytes){
	csp_channel_t chan_pack __attribute__ ((aligned(16)));

	chan_pack.op     = op; 
	chan_pack.type   = chan_type; 
	chan_pack.id     = chan_id; 
	chan_pack.addr   = (ls_addr_t) addr; 
	chan_pack.nbytes = nbytes;

	return __csp__syscall( COMM_CHANN_TALK, (ls_addr_t) &chan_pack);
}

csp_guard_list csp_create_guards(csp_channel chans[], size_t size){
	unsigned int i;
	csp_guard_list glist = (csp_guard_list_t*) malloc(sizeof(csp_guard_list));
	glist->head = (csp_channel_t*) _malloc_align(sizeof(csp_channel_t)*size, 7);

	glist->size = size;
	for(i=0; i < size; i++){
		memcpy(&glist->head[i], chans[i], sizeof(csp_channel_t));
	}
	return glist;
}


csp_channel csp_create_channel(int chan_type, uint64_t chan_id, 
		size_t nbytes) {
	csp_channel chan = (csp_channel_t*) _malloc_align(sizeof(csp_channel_t), 7);
	chan->type = chan_type;
	chan->id = chan_id;
	chan->nbytes = (ls_size_t) nbytes;
	return chan;
}


int csp_alt_select(csp_guard_list guards, void *inputs[], size_t n_inputs){
	unsigned int i;
	//csp_channel_t *head;

	ASSERT( guards->size == n_inputs );

	for(i=0; i < n_inputs; i++){
		guards->head[i].addr = (ls_addr_t) inputs[i];
		guards->head[i].op = CHANNEL_READ;
	}
	return __csp__syscall( ALT, (ls_addr_t) guards);
}

void csp_poison_channel(csp_channel channel){
	printf("new: poison channel\n");
//	__csp__syscall( POISON, (ls_addr_t) channel);
	(*kernel_syscalls->csp_poison_channel)((ls_addr_t) channel);
}


/*
int csp_chan_write(int chan_type, uint64_t chan_id, void *addr, size_t nbytes){
	return __csp_chan_comm(CHANNEL_WRITE, chan_type, chan_id, addr, nbytes);
}

int csp_chan_read(int chan_type, uint64_t chan_id, void *addr, size_t nbytes){
	return __csp_chan_comm(CHANNEL_READ, chan_type, chan_id, addr, nbytes);
}
*/

int csp_channel_read(csp_channel chan, void *addr){
	return __csp_chan_comm(CHANNEL_READ, chan->type, chan->id, addr, chan->nbytes);
}

int csp_channel_write(csp_channel chan, void *addr){
	return __csp_chan_comm(CHANNEL_WRITE, chan->type, chan->id, addr, chan->nbytes);
}


void csp_exit_network(){
	//__csp__syscall( COMM_DESTROY, 0 );	
	printf("new: exit network\n");
	(*kernel_syscalls->csp_exit_network)();
}


size_t csp_code_size(){
	return (*kernel_syscalls->csp_code_size)();
	//return __csp__syscall( GET_CODE_SIZE, 0 );
}


ea_addr_t csp_shared_addr(){
	return (*kernel_syscalls->csp_shared_addr)();
	//return __csp__syscall( GET_SHARED_ADDR, 0 );
}



void csp_yield(){
	__csp__syscall( YIELD, 0 );
}

void csp_exit(int val){
	__csp__syscall( EXIT, val );
	//return (*kernel_syscalls->csp_exit)(val);
}

int csp_spe_id(){
	////return __csp__syscall( GET_SPE_ID, 0 );
	return (*kernel_syscalls->csp_spe_id)();
}

int csp_pid(){
	//return __csp__syscall( GET_PID, 0 );
	return (*kernel_syscalls->csp_pid)();
}


